Skip to content

add plugin client factory #71

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 6, 2017
Merged

add plugin client factory #71

merged 1 commit into from
Jul 6, 2017

Conversation

fbourigault
Copy link
Contributor

@fbourigault fbourigault commented May 11, 2017

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Related tickets partially php-http/HttplugBundle#109
Documentation TODO
License MIT

This is required to display plugins created by 3rd party libraries in the HttplugBundle profiler. See php-http/HttplugBundle#109 (comment) for all puzzle pieces.

To Do

  • Update the changelog.
  • Update the documentation.

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option parameter is arbitrary, there is no way to make plugin clients be interchangeable. Should we define the options? Do we need options at all in the plugin client factory?

namespace Http\Client\Common;

/**
* Create a PluginClient.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have a more expressive comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe remove this class comment and only use the one on the interface.

use Http\Client\HttpClient;

/**
* Create a PluginClient.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to any suggestion :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

Create a PluginClient object loaded with an array of plugins. A factory may add or modify the plugins or change the HttpClient.

* @param Plugin[] $plugins
* @param array $options
*
* @see PluginClient constructor for $options details.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we cannot "see the PluginClient". We are not aware of any client implementation. We should write something like "array of options that are passed to the client."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. There is only one PluginClient without a dedicated interface and this implementation is final. There is no way to write an other PluginClient!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are correct. I had in mind that one could create ANY PluginClient with this. But the PluginClientFactoryInterface creates an instance of the concrete class PluginClient. That is a huge different from our other *FactoryInterfaces. They create an instance of an interface.

@Nyholm
Copy link
Member

Nyholm commented May 12, 2017

Note:
The PluginClientFactoryInterface creates an instance of the concrete class PluginClient. That is a huge different from our other *FactoryInterfaces. They create an instance of an interface.

@sagikazarmark
Copy link
Member

TBH, I don't really see the point of this interface.

@sagikazarmark
Copy link
Member

I don't see any alternative implementation options, so why an interface?

Creating a plugin client is configuration which is always specific to the application, so why standardizing it?

@fbourigault
Copy link
Contributor Author

The idea behind is to use discovery to find a concrete PluginClientFactory. When the bundle profiling is enabled, a profile aware PluginClientFactory is decorates each plugin with a ProfilePlugin and add the StackPlugin at the begining. See php-http/HttplugBundle#109 (comment) for a whole plan details.

@sagikazarmark
Copy link
Member

Why do we need a factory to decorate plugins? And why do we have to discover it? I still think it's configuration: when profiling is enabled, hook into DI and wrap plugins, otherwise don't.

@fbourigault
Copy link
Contributor Author

fbourigault commented May 12, 2017

It's all about displaying plugins from 3rd party like php-github-api in the profiler. With such implementation, you can integrate any 3rd party library (which use this interface and the discovery) in the profiler without having any specific code in the 3rd party library, without having a glue bundle and without having to write a line of configuration!

@fbourigault
Copy link
Contributor Author

fbourigault commented May 12, 2017

I updated the PR to add a note in the changelog. I also adressed @Nyholm comments about docblocks. I added in my to do list the puli stuff. Can someone help me with it?

EDIT: I think I found what I have to do with puli. Does I need to add some tests related to puli?

@fbourigault
Copy link
Contributor Author

How can I test puli resources declaration?

@fbourigault
Copy link
Contributor Author

This is now updated to provide a solution which does not require php-http/discovery to profile 3rd party library clients.

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand better now why we use the callable. We loose a lot of code and interfaces.

* Set the factory to use. The callable to provide must have the same arguments and return type as
* PluginClientFactory::createClient.
*
* @internal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe state why?

This is used by the HTTPlugBundle to provide a better Symfony integration

*/
public function createClient($client, array $plugins = [], array $options = [])
{
$factory = static::$factory ?: function ($client, array $plugins, array $options) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not simply do:

if (static::$factory) {
  return $factory($client, $plugins, $options);
}

return new PluginClient($client, $plugins, $options);

It would be easier to read.

@fbourigault
Copy link
Contributor Author

Thank you @Nyholm. I addressed all your remarks.

Copy link
Member

@Nyholm Nyholm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good, just fix the travis error

public function createClient($client, array $plugins = [], array $options = [])
{
if (static::$factory) {
return (static::$factory)($client, $plugins, $options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not allowed in PHP 5.x. ( I just found out)

$func = static::$factory;
return $func($client, $plugins, $options);

// Or: (This might work)
return static::$factory($client, $plugins, $options); 

@fbourigault
Copy link
Contributor Author

Should be fixed now. Please, don't merge until it's ok on other packages.

@Nyholm
Copy link
Member

Nyholm commented Jul 1, 2017

@fbourigault Is this ready to merge?

Do @dbu or @sagikazarmark want to have a final review?

Copy link
Member

@sagikazarmark sagikazarmark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why we need this, but I am still not a huge fan.

/**
* @var callable
*/
private static $factory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the workaround for not having an interface? If so, having an interface for this is still less evil. This solution provides zero type safety (even if the support PHP versions do not support return types).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the stuff is marked internal, we choosed to avoid over engineering and to not use an interface.

@fbourigault
Copy link
Contributor Author

I added the client_name option.

@Nyholm
Copy link
Member

Nyholm commented Jul 6, 2017

I will merge this, but we should review it a final time before releasing.

Thank you for the comments and thank you for the work on this PR

@Nyholm Nyholm merged commit fd4fd0a into php-http:master Jul 6, 2017
@Nyholm Nyholm mentioned this pull request Jul 6, 2017
if (static::$factory) {
$factory = static::$factory;

return $factory($client, $plugins, $options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deos simply return static::$factory(...) not work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only in PHP 7+

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird. can you add a comment that this is to work with php 5 and can be removed in 7?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wrong. It doesn't work in any PHP version: "Function name must be a string".
See https://3v4l.org/WA5ek.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright. then maybe a comment that using it directly does not work at least up to php 7.1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't work too in 7.2.0alpha2. IMHO it doesn't worth to write a comment about something not supported by the language itself.

*
* @return PluginClient
*/
public function createClient($client, array $plugins = [], array $options = [])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this method dynamic but the factory callable static? this means i must instantiate the factory, but i can only set one single callable. why not make the callable an optional constructor argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setFactory is static as we need to change the implementation at runtime when bundle profiling is enabled and, as we aim to do 0config integration, we can't have something non-static here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, i see. maybe a bit of comment in the code would help so we remember in the future ;-)

@dbu
Copy link
Contributor

dbu commented Jul 10, 2017

could we use the class docblock to explain why this factory should be used?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants